/*
Copyright 2008, 2009 Joonas Lehtolahti

This file is part of Shared Datatypes and Utilities.

Shared Datatypes and Utilities is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Shared Datatypes and Utilities is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with Shared Datatypes and Utilities.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "non0string.h"
#include <string.h>


/**
 * n0salloc
 *  Allocates memory for a non0string structure
 *
 * Parameters:
 *  v_size  the size to give to the char array inside
 * 
 * Returns:
 *  the pointer to the freshly allocated non0string structure
 *  NULL in case of error
 */
DLLEXPORT(non0string*) n0salloc(size_t v_size) {
	unsigned char* v = (unsigned char*)dmalloc((v_size+1)*sizeof(unsigned char));
	non0string* x = (non0string*)dmalloc(sizeof(non0string));
	if (!v || !x) {
		dfree(v);
		n0sfree(x);
		return NULL;
	}
	v[v_size] = '\0'; /* artificial terminator for printf */
	x->s = v;
	x->l = v_size;
	return x;
	/* Remember to free the memory also! */
}

/**
 * n0sfree
 *  Frees memory from a non0string structure
 *
 * Parameters:
 *  v  the non0string structure to be freed
 * 
 * Returns:
 *  nothing
 */
DLLEXPORT(void) n0sfree(non0string *v) {
	if (v) {
		dfree(v->s);
		dfree(v);
	}
}


/**
 * n0strstr
 *  Function to locate a substring inside a non0string
 *
 * Parameters:
 *  nstr  a pointer to non0string structure to examine
 *  zstr  a pointer to zero-terminated char array to find
 *
 * Returns:
 *  pointer to the location in 's' element of nstr where the
 *  first instance of zstr was found in.
 *  NULL if not found
 */
DLLEXPORT(unsigned char*) n0strstr(non0string* nstr, const unsigned char* zstr) {
	unsigned char *walker1, *stopper1, *retpos;
	const unsigned char *walker2, *stopper2;
	if (nstr == NULL || zstr == NULL) return NULL;
	
	walker1 = (unsigned char*)nstr->s;
	walker2 = (unsigned char*)zstr;
	stopper1 = walker1 + nstr->l;
	stopper2 = walker2 + strlen((const char*)zstr);
	retpos = NULL;
	while (walker1!=stopper1) {
		if (*walker1 == *walker2) {
			if (!retpos) retpos = walker1;
			walker2++;
			if (walker2==stopper2) return retpos;
		}
		else {
			walker2 = zstr;
			retpos = NULL;
		}
		walker1++;
	}
	return NULL;
}


/**
 * n0strcmp
 *  Function to compare two non0string values
 *
 * Parameters:
 *  str1
 *  str2
 *
 * Returns:
 *  <0  if str1 is before str2
 *   0  if str1 is equal to str2
 *  >0  if str1 is after str2
 */
DLLEXPORT(int) n0strcmp(non0string *str1, non0string *str2) {
	unsigned char *walker1, *walker2, *stopper1, *stopper2;
	int retval = 0;
	
	/*fprintf(stderr, "n0strcmp(%p, %p)\n", str1, str2);
	DumpObject(str1->s, str1->l);
	DumpObject(str2->s, str2->l);*/
	
	walker1 = str1->s;
	walker2 = str2->s;
	stopper1 = walker1 + str1->l;
	stopper2 = walker2 + str2->l;
	while (walker1 != stopper1 && walker2 != stopper2) {
		retval = *walker1++ - *walker2++;
		if (retval) return retval;
	}
	return str1->l-str2->l;
}

/**
 * char2non
 *  Helper function to convert a char* to non0string*
 *
 * Parameters:
 *  x  zero-terminated string
 *
 * Returns:
 *  pointer to non0string object that contains the string as value
 */
DLLEXPORT(non0string*) char2non(const unsigned char* x) {
	if (!x) return NULL;
	else {
		int nl = strlen((char*)x);
		non0string *rv = n0salloc(nl);
		memcpy(rv->s, x, rv->l);
		return rv;
	}
}

/**
 * n0scopy
 *   Copies contents of a non0string to a new one
 *
 * Parameters:
 *   x  existing non0string object
 *
 * Returns:
 *   pointer to newly allocated non0string object with the same data as x
 */
DLLEXPORT(non0string*) n0scopy(non0string* x) {
	if (!x) return NULL;
	else {
		non0string *rv = n0salloc(x->l);
		memcpy(rv->s, x->s, rv->l);
		return rv;
	}
}

/**
 * non2char
 *  Helper function to convert a non0string* to char*
 *
 * Parameters:
 *  x  pointer to non0string object
 *
 * Returns:
 *  zero-terminated string that contains the value
 */
DLLEXPORT(unsigned char*) non2char(non0string* x) {
	if (!x) return NULL;
	else {
		unsigned char *rv = (unsigned char*)dmalloc(x->l+1);
		memcpy(rv, x->s, x->l);
		rv[x->l]=0;
		return rv;
	}
}

/**
 * n0tou (non0string *source)
 *   converts source string into unsigned integer
 * 
 * Parameters:
 *   source  source string
 * 
 * Returns:
 *   parsed unsigned int
 * 
 * Notes:
 *   stops parsing on first character not in base
 *   allows prefixes 0x and 00 for hex and octal,
 *   otherwise considered to be decimal
 */
DLLEXPORT(unsigned int) n0tou(non0string *source) {
	return (unsigned int)n0tolu(source);
}

/**
 * n0tolu (non0string *source)
 *   converts source string into unsigned long long integer
 * 
 * Parameters:
 *   source  source string
 * 
 * Returns:
 *   parsed unsigned long long int
 * 
 * Notes:
 *   stops parsing on first character not in base
 *   allows prefixes 0x and 00 for hex and octal,
 *   otherwise considered to be decimal
 */
DLLEXPORT(unsigned long long int) n0tolu(non0string *source) {
	unsigned long long int base = 10;
	unsigned long long int final = 0;
	unsigned char *start = source->s;
	if (source->l == 0) {
		return 0;
	}
	if (source->l > 2) {
		if (start[0] == '0') {
			if (start[1] == 'x') {
				base = 16;
				++start;
				++start;
			}
			else if (start[1] == '0') {
				base = 8;
				++start;
				++start;
			}
		}
	}
	while (start) {
		unsigned long long int temp = 0;
		if ('0' <= *start && *start <= '9') {
			temp = *start-'0';
		}
		else if ('A' <= *start && *start <= 'Z') {
			temp = *start-'A'+10;
		}
		else if ('a' <= *start && *start <= 'z') {
			temp = *start-'a'+10;
		}
		else {
			break;
		}
		if (temp>=base) {
			break;
		}
		final = (final * base) + temp;
		++start;
	}
	return final;
}
